﻿using System.IO;
using System.Threading;
using System.Net.DnsToolkit.Bastard.PacketGenerator;
using System.Net.DnsToolkit.Packet;
using System.Net.DnsToolkit.Resolver;
using System.Net.DnsToolkit.Serializer;
using System.Net.DnsToolkit.SocketAsync;

namespace System.Net.DnsToolkit.Bastard {

    public class LousyBastard {

        private readonly IDnsServerProvider _serverProvider;
        private readonly IMessageReader<Stream> _messageReader;
        private readonly IMessageWriter<TextWriter> _messageWriter;
        private readonly UdpClientAsync _socket;
        private readonly AutoResetEvent _ev;

        const int dnsport = 53;

        public LousyBastard(
            IDnsServerProvider serverProvider,
            UdpClientAsync socket,
            IMessageReader<Stream> messageReader,
            IMessageWriter<TextWriter> messageWriter) {

            _serverProvider = serverProvider;
            _messageReader = messageReader;
            _messageWriter = messageWriter;

            _socket = socket;
            _socket.OnReceiveCompleted += OnReceiveCompleted;

            _ev = new AutoResetEvent(false);

        }

        public IMessage Scratch(IPacketGenerator packet) {

            IPAddress[] servers = _serverProvider.Provide();

            var remote = new IPEndPoint(servers[0], dnsport);
            var ticket = new Ticket();

            byte[] bs = packet.GetBytes();
            _socket.SendTo(remote, bs, 0, bs.Length, ticket);

            _ev.WaitOne(TimeSpan.FromSeconds(11));

            return ticket.Message;

        }

        class Ticket {
            public IMessage Message { get; set; }
            public bool Success { get; set; }
        }


        void OnReceiveCompleted(object sender, UdpClientEventArgs e) {

            if (e.HasException) {
                e.Treated = true;
                return;
            }

            IMessage message;
            var ticket = e.Ticket as Ticket;

            using (var stream = new MemoryStream(e.Buffer, e.Offset, e.Length)) {
                bool read = _messageReader.TryRead(stream, out message);
                ticket.Message = message;
                ticket.Success = read;
            }

            _ev.Set();

        }

    }
}
